package org.example.wax.manager;

import com.alibaba.fastjson2.JSON;
import com.example.cabinet.lock.lib.LockControl;
import com.example.sixteen.jt.lib.SelMark;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.example.wax.constant.StatusConstant;
import org.example.wax.domain.*;
import org.example.wax.exception.ServiceException;
import org.example.wax.manager.listener.OnCabinetManagerListener;
import org.example.wax.properties.CabinetProperties;
import org.example.wax.utils.BytesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

public class CabinetManager {

    private static final Logger logger = LoggerFactory.getLogger(CabinetManager.class);

    private RestTemplate restTemplate = new RestTemplate();

    /**
     * 读写器
     */
    private Map<Integer, SixteenReader> readerMap = new ConcurrentHashMap<>();

    /**
     * 锁控
     */
    private Map<Integer, SixteenLock> lockMap = new ConcurrentHashMap<>();

    public static CabinetManager instance;

    ExecutorService executor = Executors.newFixedThreadPool(4);

    private CabinetProperties properties;

    private ReentrantLock reentrantLock = new ReentrantLock();

    /**
     * 缓存多个读写器的盘点记录
     */
    private List<MarkItem> invResult = new ArrayList<>();

    /**
     * 缓存多个读写器是否盘点完成
     */
    private Map<Integer, Boolean> invCompleted = new ConcurrentHashMap<>();

    public static CabinetManager getInstance() {
        synchronized (CabinetManager.class) {
            if (instance == null) {
                synchronized (CabinetManager.class) {
                    instance = new CabinetManager();
                }
            }
        }
        return instance;
    }

    public void initCabinet(CabinetProperties properties) {
        this.properties = properties;

        String os = System.getProperty("os.name");
        String dir = System.getProperty("user.dir");
        String filename = "";
        if (os.startsWith("windows")) {
            filename = dir + "\\layout.json";
        } else {
            filename = dir + "/layout.json";
        }
        File file = new File(filename);
        if (!file.exists()) {
            throw new ServiceException(filename + "不存在");
        }
        String json = "";
        try {
            FileInputStream inputStream = new FileInputStream(file);
            Reader reader = new InputStreamReader(inputStream);
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            inputStream.close();
            reader.close();
            json = sb.toString();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        CabinetLayout layout = JSON.parseObject(json, CabinetLayout.class);
        if (layout == null) {
            throw new ServiceException("layout.json格式错误");
        }

        for (int i = 0; i < layout.getReader().size(); i++) {
            CabinetLayout.ReaderItem item = layout.getReader().get(i);
            SixteenReader reader = new SixteenReader(item.getIp(), i, item.getAntIndexes(), item.getBoxIndexes());
            reader.checkLayout();
            reader.setOnCabinetManagerListener(listener);
            readerMap.put(i, reader);
        }

        for (int i = 0; i < layout.getLock().size(); i++) {
            CabinetLayout.LockItem item = layout.getLock().get(i);
            SixteenLock lock = new SixteenLock(item.getIp(), i, item.getCabinetId(), item.getBoxIndexes(), item.getPortIndexes());
            lock.checkLayout();
            lock.setOnCabinetManagerListener(listener);
            lockMap.put(i, lock);
        }
    }

    public void connectReader() {
        for (SixteenReader reader : readerMap.values()) {
            reader.connectEx();
        }
    }

    public void disconnectReader() {
        for (SixteenReader reader : readerMap.values()) {
            reader.disconnectEx();
        }
    }

    public void connectLockControl() {
        for (SixteenLock lock : lockMap.values()) {
            lock.connectEx();
        }
    }

    public void disconnectLockControl() {
        for (SixteenLock lock : lockMap.values()) {
            lock.disconnectEx();
        }
    }

    public void inventory(InventoryBody inventoryBody) {
        List<SixteenReader> readers = new ArrayList<>();
        for (SixteenReader reader : readerMap.values()) {
            int channels = reader.getChannels(inventoryBody.getBoxes());
            logger.info("reader: {}, channels={}", reader.getHost(), channels);
            if (channels > 0) {
                if (reader.getStatus().getStatus().equalsIgnoreCase(StatusConstant.STATUS_DISCONNECTED)) {
                    String message = String.format("RFID接收器(%s)未连接", reader.getHost());
                    throw new ServiceException(message);
                }
                if (reader.getStatus().getStatus().equals(StatusConstant.STATUS_INVENTORYING)) {
                    String message = String.format("RFID接收器(%s)正在盘点中，请稍后再试", reader.getHost());
                    throw new ServiceException(message);
                }
                readers.add(reader);
            }
        }

        invResult.clear();
        invCompleted.clear();
        int maxPower = inventoryBody.getMaxPower() == null ? 33 : inventoryBody.getMaxPower().intValue();
        int maxPowerTimes = inventoryBody.getMaxPowerTimes() == null ? 1 : inventoryBody.getMaxPowerTimes().intValue();
        for (SixteenReader reader : readers) {
            int channels = reader.getChannels(inventoryBody.getBoxes());
            reader.setMultiParam(maxPower, maxPowerTimes);
            reader.inventory(inventoryBody.getEventId(), channels, inventoryBody.getPower(), inventoryBody.getInvMs(), inventoryBody.getSleepMs(), inventoryBody.getTimes());
            invCompleted.put(reader.getIndex(), false);
        }
        logger.info("inventory status: {}", JSON.toJSONString(invCompleted));
    }

    public void open(List<Integer> boxes) {
        Callable<Boolean> callable = () -> {
            /**
             * 1、检查锁控板的状态及开锁状态
             */
            for (SixteenLock lock : lockMap.values()) {
                List<Integer> ports = lock.getSelPorts(boxes);
                if (!lock.getStatus().getStatus().equals(StatusConstant.STATUS_CONNECTED)) {
                    throw new ServiceException(String.format("锁控板ip=%s未连接", lock.getIp()));
                }
                if (!CollectionUtils.isEmpty(ports)) {
                    if (lock.isLockControlBusy()) {
                        throw new ServiceException("上一笔开锁指令还在进行中，请稍后再试");
                    }
                }
            }
            /**
             * 2、盘点是否在盘点
             */
            for (SixteenReader reader : readerMap.values()) {
                int channels = reader.getChannels(boxes);
                if (channels > -1) {
                    if (reader.getStatus().getStatus().equalsIgnoreCase(StatusConstant.STATUS_INVENTORYING)) {
                        throw new ServiceException("正在盘点中，请稍后再试");
                    }
                }
            }

            /**
             * 3、将锁控板的状态设置成繁忙
             */
            for (SixteenLock lock : lockMap.values()) {
                lock.setLockControlBusy(true);
            }

            /**
             * 4、开箱并缓存状态
             */
            for (SixteenLock lock : lockMap.values()) {
                lock.stopInspect();
                List<Integer> ports = lock.getSelPorts(boxes);
                for (Integer port : ports) {
                    for (int i = 0; i < 3; i++) {
                        Thread.sleep(1000);
                        int rc = lock.open(port);
                        if (rc == LockControl.SUCCESS) {
                            break;
                        }
                    }
                }
                lock.startInspect();
            }
            for (SixteenReader reader : readerMap.values()) {
                reader.cacheOpened(boxes);
            }

            for (SixteenLock lock : lockMap.values()) {
                lock.setLockControlBusy(false);
            }
            return true;
        };
        FutureTask futureTask = new FutureTask<>(callable);
        executor.submit(futureTask);
        try {
            futureTask.get();
        } catch (Exception e) {
            throw new ServiceException(e.getCause().getMessage());
        }
    }

    public void multiLightOn(List<LightBody> bodies, int times) {
        for (SixteenReader reader : readerMap.values()) {
            List<SelMark> selected = new ArrayList<>();
            for (LightBody body : bodies) {
                if (reader.getAnt(body.getBox()) > -1) {
                    SelMark mark = new SelMark();
                    mark.setAnt(body.getAnt());
                    mark.setChannels(reader.getAnt(body.getBox()));
                    byte[] bs = BytesUtils.hex2Bytes(body.getMark());
                    mark.setMark(bs);
                    mark.setLen(bs.length);
                    selected.add(mark);
                }
            }
            if (selected.size() > 0) {
                reader.startLightMulti(selected.toArray(new SelMark[selected.size()]), times);
            }
        }
    }

    public void multiLightOff(List<Integer> boxes) {
        for (SixteenReader reader : readerMap.values()) {
            boolean sel = false;
            for (Integer box : boxes) {
                if (reader.getAnt(box) > -1) {
                    sel = true;
                    break;
                }
            }
            if (sel) {
                reader.stopLightMulti();
            }
        }
    }

    private OnCabinetManagerListener listener = new OnCabinetManagerListener() {
        @Override
        public void onInventory(int index, String eventId, List<MarkItem> items) {
            StringBuilder sb = new StringBuilder();
            sb.append("----Inventory Result: ----").append("\n");
            sb.append("marks=").append(items.size()).append("\n");
            List<Integer> boxes = items.stream().map(MarkItem::getBox).distinct().collect(Collectors.toList());
            for (Integer box : boxes) {
                List<MarkItem> list = items.stream().filter(x -> x.getBox().intValue() == box.intValue()).collect(Collectors.toList());
                sb.append("#box").append(box).append("=").append(list.size()).append("\n");
            }
            logger.info("{}", sb.toString());
            reentrantLock.lock();
            if (invCompleted.containsKey(index)) {
                invCompleted.put(index, true);
            }
            logger.info("inventory status: {}", JSON.toJSONString(invCompleted));
            invResult.addAll(items);
            if (!invCompleted.values().contains(false)) {
                Map<String, Object> body = new HashMap<>();
                body.put("sn", properties.getSn());
                body.put("eventId", eventId);
                body.put("marks", invResult);
                postCallback(properties.getInventoryCallback(), body);
            }
            reentrantLock.unlock();
        }

        @Override
        public void onClosed(int box) {
            List<Integer> needInvBoxes = new ArrayList<>();
            boolean isAllClosed = false;
            boolean isNeedPost = false;
            for (SixteenReader reader : readerMap.values()) {
                needInvBoxes.addAll(reader.getNeedInvBoxes());
                if (reader.cacheClosed(box) > -1) {
                    isNeedPost = true;
                }
            }
            if (isNeedPost) {
                for (SixteenReader reader : readerMap.values()) {
                    isAllClosed = reader.isAllClosed();
                    if (!isAllClosed) {
                        break;
                    }
                }
                Map<String, Object> body = new HashMap<>();
                body.put("sn", properties.getSn());
                body.put("box", box);
                body.put("isAllClosed", isAllClosed);
                body.put("needInvBoxes", needInvBoxes);
                postCallback(properties.getClosedCallback(), body);
            }
        }
    };

    private void postCallback(String callback, Object body) {
        Callable<String> callable = () -> {
            HttpHeaders headers = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            HttpEntity<Object> entity = new HttpEntity<>(body, headers);
            ResponseEntity<String> result = restTemplate.postForEntity(callback, entity, String.class);
            logger.info(result.getBody());
            return result.getBody();
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        executor.submit(futureTask);
    }

    public NetworkResult getStatus() {
        NetworkResult result = new NetworkResult();
        List<NetworkStatus> receiverStatus = new ArrayList<>();
        for (SixteenReader reader : readerMap.values()) {
            receiverStatus.add(reader.getStatus());
        }
        List<NetworkStatus> lockStatus = new ArrayList<>();
        for (SixteenLock lock : lockMap.values()) {
            lockStatus.add(lock.getStatus());
        }
        result.setReader(receiverStatus);
        result.setLock(lockStatus);
        return result;
    }
}
